#pragma once

#include "ColligereSingleton.hpp"
#include "ColligereUtils.hpp"

namespace col {

class ControlLink
{
public:
	struct Type
	{
		enum
		{
			NormalInputField = 1,
			ListCtrl,
			PropertyGrid
		};
	};

	ControlLink() : mCtrlColIdx(-1), mType(0), mId(-1) { }
	
	ControlLink ( const wxString &rXrcId ) : mXrcId ( rXrcId ), 
		mCtrlColIdx (-1), mType ( Type::NormalInputField ), 
		mId ( XRCID ( rXrcId.c_str() ) )
	{ 
	}
	
	ControlLink ( wxUint32 propGridId, const wxString &rPropName ) 
		: mId ( propGridId ), mPropName ( rPropName ),
		mCtrlColIdx(-1), mType ( Type::PropertyGrid ) 
	{
		wxASSERT ( !mPropName.empty() );
	}
	
	ControlLink ( const wxString &rXrcId, wxUint32 ctrlColIdx ) 
		: mId ( XRCID ( rXrcId.c_str() ) ), mXrcId ( rXrcId ),
		mType ( Type::ListCtrl ), mCtrlColIdx ( ctrlColIdx )
	{
		wxASSERT ( mCtrlColIdx > -1 );
	}

	const wxString &GetXrcId() const { return mXrcId; }
	wxInt32 GetId() const { return mId; }
	wxInt32 GetColumnIndex() const { return mCtrlColIdx; }
	wxUint32 GetType() const { return mType; }
	const wxString GetPropName() const { return mPropName; }

	bool IsXrcCtrl() const { return !mXrcId.empty(); }
private:
	wxString mXrcId, mPropName;
	wxInt32 mCtrlColIdx, mId;
	wxUint32 mType;
};

/*! @brief This class represents an SQL database table.
 **/
class DatabaseTable
{
public:
	//! Default constructor.
	DatabaseTable();
	/*! @brief Constructor.
	 *
	 * @param rName The name of the table.
	 *
	 * @param ownerDatabaseTypeId The type ID of the owner database.
	 *
	 * @param rParentTableName The name of the parent table
	 *        (e.g. the table "albums" is a child table of the "artists" table).
	 *
	 * @param noAutoIdColumn The table will have an automatic ID column unless
	 *                       this is set to false.
	 **/
	DatabaseTable ( const wxString &rName, wxUint32 ownerDatabaseTypeId, 
		const wxString &rParentTableName = wxString(), bool noAutoIdColumns = false );

	//! This class represents a column in an SQL database table.
	class Column
	{
	public:
		//! Default constructor.
		Column();

		Column ( wxUint32 indx, const wxString &rName, wxUint32 type, 
			wxUint32 constraints = 0, bool isParentIdColumn = false, 
			wxInt32 orderByPosition = -1 );

		//! Constraint enum wrapper.
		struct Constraint
		{
			enum
			{
				NotNull			= 1 << 1,
				PrimaryKey		= 1 << 2,
				Unique			= 1 << 3,
				AutoIncrement	= 1 << 4
			};
		};

		//! Type enum wrapper.
		struct Type
		{
			enum { Integer, Text };
		};

		bool operator < ( const Column &rCol ) const;
		bool operator == ( wxInt32 colIndex ) const;
		bool operator == ( const wxString &rName ) const;
		bool operator == ( const Column &rCol ) const;

		const wxString &GetName() const;
		const ControlLink &GetCtrlLink() const;
		
		wxInt32 GetConstraints() const;
		wxInt32 GetType() const;
		wxInt32 GetIndex() const;
		wxInt32 GetOrderByPosition() const;

		void SetControlLink ( const ControlLink &rLink ) { mCtrlLink = rLink; }

		bool HasConstraint ( wxUint32 constraint ) const;

		const wxString ToString() const;

		bool IsIdColumn() const;
		bool IsParentIdColumn() const;
		bool IsInvalid() const;
	private:
		const wxString FormatConstraintsToString() const;
		const wxString FormatTypeToString() const;

		wxInt32 mIndx, mType, mOrderByPosition, mConstraints;
		ControlLink mCtrlLink;
		wxString mName;
		bool mIsParentIdColumn;
	};

	//! This class represents a trigger in an SQL database table.
	class Trigger
	{
	public:
		Trigger ( const wxString &rName, wxUint32 eventType, 
			wxUint32 execPosition, const wxString &rTriggerSteps, 
			const wxString &rParentTable );

		//! Execution position enum wrapper.
		struct ExecPosition
		{
			enum { After, Before };
		};

		//! Even type enum wrapper.
		struct EventType
		{
			enum { Delete, Insert, Update, UpdateOf };
		};

		//! Returns the name of the trigger.
		const wxString &GetName() const;
		//! Returns the name of the parent table for this trigger.
		const wxString &GetParentTableName() const;
		//! Returns the SQL part which will be executed when this trigger is triggered.
		const wxString &GetTriggerSteps() const;

		//! Returns the event type for this trigger.
		wxUint32 GetEventType() const;
		//! Returns the execution position for this trigger.
		wxUint32 GetExecPosition() const;

		//! Returns the information from this trigger as an valid SQL CREATE TRIGGER query.
		const wxString ToSqlCreateTriggerQuery();

		bool operator == ( const Trigger &rCmp ) const;
	private:
		//! The name of the trigger.
		wxString mName;
		//! The name of the parent database table.
		wxString mParentTableName;
		//! The SQL part which will be executed when this trigger is triggered.
		wxString mTriggerSteps;

		//! The event type for this trigger.
		wxUint32 mEventType;
		//! The execution position for this trigger.
		wxUint32 mExecPosition;
	};

	typedef std::deque < Column > ColumnDeque;
	typedef std::deque < Trigger > TriggerDeque;

	/*! @brief Registers a new column to the column list.
	 *
	 * @param rCol The new column
	 * @return *this
	 **/
	DatabaseTable &RegisterColumn ( const Column &rCol );
	/*! @brief Registers a new trigger to the trigger list.
	 *
	 * @param rTr The new trigger
	 * @return *this
	 **/
	DatabaseTable &RegisterTrigger ( const Trigger &rTr );

	//! @see RegisterColumn
	DatabaseTable &operator += ( const Column &rCol );
	//! @see RegisterTrigger
	DatabaseTable &operator += ( const Trigger &rCol );

	bool operator == ( const DatabaseTable &rCmp ) const;

	const wxString &GetName() const;
	const wxString &GetParentName() const;
	const wxString GetItemName() const;
	const Column &GetColumn ( wxUint32 index ) const;
	Column &GetColumn ( wxUint32 index );

	/**!
	 * @param index The index of the column.
	 * @return The name of the column or an empty string if there's no such column.
	 **/
	const wxString &GetColumnNameByIndex ( wxUint32 index ) const;
	/**!
	 * @param rName The name of the column.
	 * @return The index of the column or wxNOT_FOUND if there's no such column.
	 **/
	wxInt32 GetColumnIndexByName ( const wxString &rName ) const;
	size_t GetColumnCount() const;
	size_t GetTriggerCount() const;
	wxUint32 GetOwnerDatabaseTypeId() const;
	const Column &GetParentIdColumn() const;
	const Column &GetIdColumn() const;
	//! Returns the list of columns for this table.
	const ColumnDeque &GetColumns() const;
	const TriggerDeque &GetTriggers() const;

	void Create ( const wxString &rName,
		wxUint32 ownerDatabaseTypeId, const wxString &rParentTableName = wxString(),
		bool noAutoIdColumns = false );

	//! Returns true if this table has an column which points to an parent column in a parent database table, false otherwise.
	bool HasParentIdColumn() const;

	const wxString ToSqlCreateTableQuery ( bool includeTriggers = true );
	const wxString ToSqlSelectAllQuery() const;
	const wxString ToSqlSelectAllQuery ( wxUint32 parentDbId ) const;
private:
	//! The list of columns for this table.
	ColumnDeque mColumns;
	//! The list of triggers for this table.
	TriggerDeque mTriggers;
	//! The name of this table.
	wxString mName;
	//! The name of the parent table for this table (can be empty).
	wxString mParentTableName;

	wxUint32 mOwnerDatabaseTypeId;

	static Column msInvalidColumn;
};

typedef std::deque < DatabaseTable > DatabaseTableDeque;
typedef DatabaseTable::ColumnDeque DatabaseTableColumnDeque;

class DatabaseTableListHandler : public Singleton < DatabaseTableListHandler >
{
public:
	virtual ~DatabaseTableListHandler() { }

	void RegisterTable ( const DatabaseTable &rTable );

	const DatabaseTableDeque GetTableList ( wxUint32 dbType ) const;
	bool GetTable ( wxUint32 dbType, const wxString &rName, DatabaseTable &rTo ) const;
	DatabaseTable *GetTable ( wxUint32 dbType, const wxString &rName );
	const DatabaseTableDeque &GetAllTables() const { return mTableList; }
private:
	friend class Singleton < DatabaseTableListHandler >;

	DatabaseTableDeque mTableList;
};

}